﻿using System;
using BITS.PreCompiler.CSharp.Syntax;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Roslyn.Compilers.CSharp;

namespace BITS.PreCompiler.CSharp.Tests.Syntax
{
    [TestClass]
    public partial class TestClass
    {
        [TestMethod]
        public void Create_Class()
        {
            var @class = new Class("MyClass");

            var expectet = Comparer.Create_Class();
            var actual   = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        [TestMethod]
        public void Create_Class_With_Constructor()
        {
            var @class = new Class("MyClass")
            {
                new Constructor
                {
                    @"this.Init();",
                },
            };

            var expectet = Comparer.Create_Class_With_Constructor();
            var actual   = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        [TestMethod]
        public void Create_Class_With_Members()
        {
            var @class = new Class("MyClass")
            {
                new Property("PropertyOne")
                    .WithAutoAccessors(),

                new Constructor(),

                new Method("MethodOne")
                {
                    (dynamic @this) =>
                    {
                        var result = @this.PropertyOne;
                        Console.WriteLine(result);
                    },
                },
            };

            var expectet = Comparer.Create_Class_With_Members();
            var actual   = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        [TestMethod]
        public void Create_Class_With_Constructor_And_Base_Call()
        {
            var @class = new Class("MyClass")
            {
                new Constructor
                {
                    new BaseInitializer
                    {
                        @"new Object()",
                    },

                    @"this.Init();",
                },
            };

            var expectet = Comparer.Create_Class_With_Constructor_And_Base_Call();
            var actual   = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        [TestMethod]
        public void Create_Class_With_Constructor_And_This_Call()
        {
            var @class = new Class("MyClass")
            {
                new Constructor
                {
                    new ThisInitializer
                    {
                        @"new Object()",
                        @"new Object()",
                    },

                    @"this.Init();",
                },
            };

            var expectet = Comparer.Create_Class_With_Constructor_And_This_Call();
            var actual   = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        [TestMethod]
        public void Create_Class_With_Constructor_And_Parameters()
        {
            var @class = new Class("MyClass")
            {
                new Constructor(parameters: "String args1")
                {
                    @"this.Init();",
                },
            };

            var expectet = Comparer.Create_Class_With_Constructor_And_Parameters();
            var actual   = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        [TestMethod]
        public void Create_Class_With_String_Members()
        {
            var @class = new Class("MyClass")
            {
                @"public int TestPropertyA { get; set; }",
                
                @"public string TestPropertyB { get; set; }",
                
                @"public int TestMethodA()
                {
                    var x = 0;

                    return x;
                }",
            };

            var expectet = Comparer.Create_Class_With_String_Members();
            var actual   = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        [TestMethod]
        public void Create_Class_With_Methods()
        {
            var typeA = new Class("TypeA");

            var @private_MyClass_MethodA = new Method(typeA, identifier: "MethodA");
            var @private_MyClass_MethodB = new Method(@return: "NewClass", identifier: "MethodA");

            var @class = new Class("MyClass")
            {
                @private_MyClass_MethodA,
                @private_MyClass_MethodB,
            };

            var expectet = Comparer.Create_Class_With_Methods();
            var actual = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        //[TestMethod]
        //public void Create_Class_With_Regions()
        //{
        //    var typeA = new Class("TypeA");

        //    var @private_MyClass_MethodA = new Method(typeA, identifier: "MethodA");
        //    var @private_MyClass_MethodB = new Method(@return: "NewClass", identifier: "MethodA");

        //    var @class = new Class("MyClass")
        //    {
        //        @private_MyClass_MethodA,
        //        @private_MyClass_MethodB,
        //    };

        //    var expectet = Comparer.Create_Class_With_Regions();
        //    var actual = @class.ToString();

        //    Assert.AreEqual(expectet, actual);
        //    Console.WriteLine(actual);
        //}

        [TestMethod]
        public void Create_Class_With_Methods_If_Condition_Is_True()
        {
            var condition = "A";
            var typeA = new Class("TypeA");
            var @private_MyClass_MethodA = new Method(typeA, identifier: "MethodA");
            var @private_MyClass_MethodB = new Method(@return: "NewClass", identifier: "MethodB");
            var @class = new Class("MyClass");

            @class.Add(condition == "A" 
                ? @private_MyClass_MethodA 
                : @private_MyClass_MethodB);

            var expectet = Comparer.Create_Class_With_Methods_If_Condition_Is_True();
            var actual = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        [TestMethod]
        public void Create_Class_With_Properties()
        {
            var typeA = new Class("TypeA");

            var @private_MyClass_PropertyA = new Property(typeA, identifier: "PropertyA")
                .WithAutoAccessors();

            var @private_MyClass_PropertyB = new Property(@return: "NewClass", identifier: "PropertyB")
                .WithAutoAccessors();

            var @private_MyClass_PropertyC = new Property(@return: "int", identifier: "PropertyC")
            {
                @"get { return 0; }",
            };

            var @class = new Class("MyClass")
            {
                @private_MyClass_PropertyA,
                @private_MyClass_PropertyB,
                @private_MyClass_PropertyC
            };

            var expectet = Comparer.Create_Class_With_Properties();
            var actual = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        [TestMethod]
        public void Create_Class_With_Properties_And_Methods()
        {
            var typeA = new Class("TypeA");

            var @private_MyClass_PropertyA = new Property(typeA, identifier: "PropertyA")
                .WithAutoAccessors();

            var @private_MyClass_PropertyB = new Property(@return: "NewClass", identifier: "PropertyA")
                .WithAutoAccessors();

            var @private_MyClass_MethodA = new Method(typeA, identifier: "MethodA");
            var @private_MyClass_MethodB = new Method(@return: "NewClass", identifier: "MethodA");

            var @class = new Class("MyClass")
            {
                @private_MyClass_PropertyA,
                @private_MyClass_PropertyB,

                @private_MyClass_MethodA,
                @private_MyClass_MethodB,
            };

            var expectet = Comparer.Create_Class_With_Properties_And_Methods();
            var actual = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        [TestMethod]
        public void Create_Class_With_String_Members_And_Strong_Typed_Members()
        {
            var typeA = new Class("TypeA");

            var @class = new Class("MyClass")
            {
                @"public int TestPropertyA { get; set; }",
                
                @"public string TestPropertyB { get; set; }",

                new Property(@return: "NewClass", identifier: "PropertyC")
                    .WithAutoAccessors(),

                new Property(@return: "NewClass", identifier: "PropertyD")
                {
                    @"get { return new NewClass(); }",
                    @"set { return null; }",
                },
                
                @"public int TestMethodA()
                {
                    var x = 0;

                    return x;
                }",

                new Method(typeA, identifier: "MethodB")
                {
                    @"int i = 0; string x;",

                    @"return null;",
                },

                new Class("InnerClassAA")
                {
                    typeA,
                },

                typeA,
            };

            var expectet = Comparer.Create_Class_With_String_Members_And_Strong_Typed_Members();
            var actual = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        [TestMethod]
        public void Create_Generic_Class()
        {
            var @class = new Class("MyClass");
            @class.AddTypeParameter("TGeneric");

            var expectet = Comparer.Create_Generic_Class();
            var actual   = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        [TestMethod]
        public void Create_Class_With_Base_Class()
        {
            var @class = new Class("MyClass");
            var baseClass = new Class("MyBaseClass");
            @class.Implements(baseClass);
            var expectet = Comparer.Create_Class_With_Base_Class();
            var actual   = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        [TestMethod]
        public void Create_Class_With_Generic_Base_Class()
        {
            var @class = new Class("MyClass");
            var baseClass = new Class("MyBaseClass");
            baseClass.AddTypeParameter("Object");
            @class.Implements(baseClass);

            var expectet = Comparer.Create_Class_With_Generic_Base_Class();
            var actual   = @class.ToString();

            Assert.AreEqual(expectet, actual);
            Console.WriteLine(actual);
        }

        public static partial class Comparer
        {
            public static String Create_Class()
            {
                return
@"public class MyClass
{
}";
            }

            public static String Create_Class_With_Constructor()
            {
                return
@"public class MyClass
{
    public MyClass()
    {
        this.Init();
    }
}";
            }

            public static String Create_Class_With_Members()
            {
                return
@"public class MyClass
{
    private Object PropertyOne { get; set; }

    public MyClass()
    {
    }

    private void MethodOne()
    {
    }
}";
            }

            public static String Create_Class_With_Constructor_And_Base_Call()
            {
                return
@"public class MyClass
{
    public MyClass() : base(new Object())
    {
        this.Init();
    }
}";
            }

            public static String Create_Class_With_Constructor_And_This_Call()
            {
                return
@"public class MyClass
{
    public MyClass() : this(new Object(), new Object())
    {
        this.Init();
    }
}";
            }

            public static String Create_Class_With_Constructor_And_Parameters()
            {
                return
@"public class MyClass
{
    public MyClass(String args1)
    {
        this.Init();
    }
}";
            }

            public static String Create_Class_With_String_Members()
            {
                return
@"public class MyClass
{
    public int TestPropertyA { get; set; }
    public string TestPropertyB { get; set; }

    public int TestMethodA()
    {
        var x = 0;

        return x;
    }
}";
            }

            public static String Create_Class_With_Methods()
            {
                return
@"public class MyClass
{
    private TypeA MethodA()
    {
    }
    private NewClass MethodA()
    {
    }
}";
            }

            public static String Create_Class_With_Methods_If_Condition_Is_True()
            {
                return
@"public class MyClass
{
    private TypeA MethodA()
    {
    }
}";
            }

            public static String Create_Class_With_Properties()
            {
                return
@"public class MyClass
{
    private TypeA PropertyA { get; set; }
    private NewClass PropertyB { get; set; }

    private int PropertyC
    {
        get
        {
            return 0;
        }
    }
}";
            }

            public static String Create_Class_With_Properties_And_Methods()
            {
                return
@"public class MyClass
{
    private TypeA PropertyA { get; set; }
    private NewClass PropertyA { get; set; }

    private TypeA MethodA()
    {
    }
    private NewClass MethodA()
    {
    }
}";
            }

            public static String Create_Class_With_String_Members_And_Strong_Typed_Members()
            {
                return
@"public class MyClass
{
    public int TestPropertyA { get; set; }
    public string TestPropertyB { get; set; }
    private NewClass PropertyC { get; set; }

    private NewClass PropertyD
    {
        get
        {
            return new NewClass();
        }

        set
        {
            return null;
        }
    }

    public int TestMethodA()
    {
        var x = 0;

        return x;
    }

    private TypeA MethodB()
    {
        int i = 0; string x;
        return null;
    }

    public class InnerClassAA
    {
        public class TypeA
        {
        }
    }

    public class TypeA
    {
    }
}";
            }

            public static String Create_Generic_Class()
            {
                return
@"public class MyClass<TGeneric>
{
}";
            }

            public static String Create_Class_With_Base_Class()
            {
                return
@"public class MyClass : MyBaseClass
{
}";
            }

            public static String Create_Class_With_Generic_Base_Class()
            {
                return
@"public class MyClass : MyBaseClass<Object>
{
}";
            }
        }
    }
}
